package network;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import network.Operations;
import java.security.NoSuchAlgorithmException;

import encryption.RSA;
import entities.Admin;
import entities.User;

import authentication.AdminAuthentication;
import authentication.ServerAuthentication;

import registration.ServerRegistration;
import voting.ServerVoting;

public class ServerConnection implements Runnable {
	Socket socket;
	private User user;
	private Admin admin;
	private String randomPassword;
	private int numOfFailedLogin;
	public ServerConnection (Socket socket) throws Exception {
		numOfFailedLogin = 0;
		this.socket = socket;
		randomPassword = null;
	}

	public void run() {
		System.out.println("New Connection");
		try {
			processRequest();
		} catch (Exception e) {
			//	e.printStackTrace();
		}
	}


	/** 
	 * Sends an RSA message
	 * @param message
	 * @return
	 */
	public boolean sendRSAMessage(byte[] message)	{
		if(socket.isClosed())
		{
			return false;
		}
		DataOutputStream outToServer;
		try {
			outToServer = new DataOutputStream(socket.getOutputStream());
			outToServer.write(message);
		} catch (IOException e) {
			try {
				socket.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * Receives an RSA as an array of bytes
	 * @return
	 */
	public byte[] receiveRSAMessage(){
		if(socket.isClosed())
		{
			return null;
		}
		DataInputStream inFromServer;
		byte[]  inMessage = new byte[256];
		try {
			inFromServer =new DataInputStream(socket.getInputStream());
			inFromServer.read(inMessage, 0, 256);
		} catch (IOException e) {
			try {
				socket.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		return inMessage;

	}
	public boolean sendMessage(String message)	{

		if(socket.isClosed())
			return false;
		DataOutputStream outToClient;
		try {
			outToClient = new DataOutputStream(socket.getOutputStream());
			outToClient.writeBytes(message+ "\n");
		} 
		catch (IOException e) {
			e.printStackTrace();
			try {
				socket.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			return false;
		}

		return true;

	}

	public String receiveMessage()
	{
		if(socket.isClosed())
			return null;
		BufferedReader inFromClient;
		String inMessage = null;

		try {
			inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			inMessage= inFromClient.readLine();
		} catch (IOException e) {
			e.printStackTrace();
			try {
				socket.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		return inMessage;

	}

	private void processRequest () throws NoSuchAlgorithmException, UnsupportedEncodingException, IOException {


		while(true)  //This loop is for demo only. It should be replaced with a sequence of steps
		{
			if(socket.isClosed())return;

			String receivedMessage = receiveMessage();
			int operation=Integer.parseInt(receivedMessage.split(" ")[0]);
			System.out.println(operation);
			switch(operation)
			{
			case Operations.REGISTER: 
				ServerRegistration register = new ServerRegistration(this);
				register.register();
				break;
			case Operations.ACTIVATE:
				ServerRegistration activate = new ServerRegistration(this);
				activate.activate();
				break;
			case Operations.LOGIN:
				numOfFailedLogin ++;

				if(numOfFailedLogin > 4){
					sendMessage(Operations.TOO_MANY_FAILED_ATTEMPS + "");
					numOfFailedLogin = 0;
					try {
						Thread.sleep(60000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					continue;
				}
				ServerAuthentication authenticate = new ServerAuthentication(this);
				authenticate.authenticate(receivedMessage.split(" ")[1]);

				break;

			case Operations.ADMIN_LOGIN:
				numOfFailedLogin ++;

				if(numOfFailedLogin > 4){
					sendMessage(Operations.TOO_MANY_FAILED_ATTEMPS + "");
					numOfFailedLogin = 0;
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					continue;
				}
				ServerAuthentication authenticateAdmin = new ServerAuthentication(this);
				authenticateAdmin.authenticateAdmin(receivedMessage.split(" ")[1]);

				break;
			case Operations.USER_VIEW :
				System.out.println("go to user view");
				User u1= new User();
				u1.setUserID(3);
				//have to send user info
				ServerVoting voting1=new ServerVoting(this,u1);
				voting1.processRequest();
				break;

			case Operations.GET_ALL_VOTES:
				User u2= new User();
				u2.setUserID(1);
				//have to send user info
				ServerVoting voting3=new ServerVoting(this,u2);
				voting3.processRequest();
				break;

			case Operations.CALCULATE_SUM:
				User u3= new User();
				u3.setUserID(1);
				//have to send user info
				ServerVoting voting4=new ServerVoting(this,u3);
				voting4.processRequest();
				break;

			case Operations.GET_CANDIDATES:
				User u4= new User();
				u4.setUserID(1);
				//have to send user info
				ServerVoting voting5=new ServerVoting(this,u4);
				voting5.processRequest();
				break;

			case Operations.ADMIN_VIEW:
				System.out.println("go to admin view");
				//have to send admin info
				Admin admin=new Admin();
				admin.setAdminID(1);
				ServerVoting voting2=new ServerVoting(this,admin);
				voting2.processRequest();
				break;
			case Operations.GET_ID:
				System.out.println("Getting the id");
				ServerAuthentication auth = new ServerAuthentication(this);
				auth.getUserID(receivedMessage.split(" ")[1]);
				System.out.println(receivedMessage.split(" ")[1]);
				ServerVoting serverVoting=new ServerVoting(this,getUser());
				serverVoting.processRequest();
				break;
			case Operations.GET_ADMIN_ID:
				System.out.println("Getting the admin id");
				ServerAuthentication auth1 = new ServerAuthentication(this);
				auth1.getAdminID(receivedMessage.split(" ")[1]);
				System.out.println(receivedMessage.split(" ")[1]);
				ServerVoting serverVoting1=new ServerVoting(this,getAdmin());
				serverVoting1.processRequest();
				break;

			case Operations.GET_PUBLIC_KEY:
				User u5= new User();
				u5.setUserID(1);
				//have to send user info
				ServerVoting voting6=new ServerVoting(this,u5);
				voting6.processRequest();
				break;
			case Operations.CA:
				CAServer CAserver = new CAServer(this); System.out.println("Created new CAServer"); CAserver.CAServerFlow();   break;

			}
		}
	}

	public void setUser(User user) {
		this.user = user;
	}

	public User getUser() {
		return user;
	}

	public void setAdmin(Admin admin) {
		this.admin = admin;
	}

	public Admin getAdmin() {
		return admin;
	}

	public void setRandomPassword(String randomPassword) {
		this.randomPassword = randomPassword;
	}

	public String getRandomPassword() {
		return randomPassword;
	}
}
